﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Grafica3
{
    public class Camera
    {


        //fromEye = Vector3.Transform(-whereIAm.View.Translation, Matrix.Transpose(whereIAm.View));
        //    toEye = Vector3.Transform(-whereToGo.View.Translation, Matrix.Transpose(whereToGo.View));

        //    var c = whereIAm.View * Matrix.CreateTranslation(-whereIAm.View.Translation);
        //    var toRot = whereToGo.View * Matrix.CreateTranslation(-whereToGo.View.Translation);

        //    Vector3 a, b;
        //    Matrix.Transpose(c).Decompose(out a, out fromQuat, out b);
        //    Matrix.Transpose(toRot).Decompose(out a, out toQuat, out b);

        //    fromField = whereIAm.FieldOfView;
        //    toField = whereToGo.FieldOfView;


        #region Consts
        public const float NEAR_PLANE = 1f;
        public const float FAR_PLANE = 1000f;
        public const float DEFAULT_FIELD_OF_VIEW = MathHelper.PiOver4;
        public const float DEFAULT_ASPECT_RATIO = 1.33333333f;
        #endregion

        #region private variables
        private string name = "Default Camera";
        private Vector3 _position = Vector3.Zero;
        private Quaternion _orientation = new Quaternion(0, 0, 1, 0);
        private float _fieldOfView = DEFAULT_FIELD_OF_VIEW;
        private float _aspectRatio = DEFAULT_ASPECT_RATIO;
        private Matrix _View;
        private Matrix _Projection;
        private BoundingFrustum _BoundingFrustum;
        private float _farPlane = FAR_PLANE;


        #endregion

        #region Properties
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public Vector3 position
        {
            get
            {
                return _position;
            }
            set
            {
                _position = value;
                recalcView();
            }
        }
        public Quaternion orientation
        {
            get
            {
                return _orientation;
            }
            set
            {
                _orientation = value;
                recalcView();
            }
        }
        public float fieldOfView
        {
            get
            {
                return _fieldOfView;
            }
            set
            {
                _fieldOfView = value;
                recalcProjection();
            }
        }
        public float AspectRatio
        {
            get
            {
                return _aspectRatio;
            }
            set
            {
                _aspectRatio = value;
                recalcProjection();
            }
        }
        public Matrix View
        {
            get
            {
                return _View;
            }
            set
            {
                _View = value;
                rePosOri();
                recalcBoundingFrustum();
            }
        }
        public Matrix Projection
        {
            get
            {
                return _Projection;
            }
            set { }
        }
        public BoundingFrustum BoundingFrustum
        {
            get { return _BoundingFrustum; }
            private set { }
        }

        public float FarPlane
        {
            get { return _farPlane; }
            set
            {
                _farPlane = value;
                recalcProjection();
            }
        }
        #endregion

        #region Constructors

        public Camera(Vector3 Position, Quaternion Orientation, float FieldOfView, float AspectRatio)
        {
            _position = Position;
            _orientation = Orientation;
            recalcView();
            _fieldOfView = FieldOfView;
            _aspectRatio = AspectRatio;
            recalcProjection();
            recalcBoundingFrustum();
        }

        private Camera()
        {
            recalcView();
            recalcProjection();
            recalcBoundingFrustum();
        }

        #endregion

        #region private methods

        //calculate th matrix view and Projection
        private void recalcProjection()
        {
            _Projection = Matrix.CreatePerspectiveFieldOfView(_fieldOfView, _aspectRatio, NEAR_PLANE, _farPlane);
            recalcBoundingFrustum();
        }
        private void recalcView()
        {
            var cameraWorld = Matrix.CreateFromQuaternion(_orientation);
            cameraWorld.Translation = _position;
            _View = Matrix.Invert(cameraWorld);
            recalcBoundingFrustum();
        }
        private void recalcBoundingFrustum()
        {
            _BoundingFrustum = new BoundingFrustum(_View * _Projection);
        }
        private void rePosOri()
        {
            _position = Vector3.Transform(-_View.Translation, Matrix.Transpose(_View));
            Vector3 a;
            Matrix.Transpose(_View).Decompose(out a, out _orientation, out a);
        }

        #endregion

        #region static variables
        //returns a instance of the default camera 
        public static Camera DefaultCamera = new Camera();
        #endregion

    }
}
